import schedulegui.data.InfoItem;
import schedulegui.gui.GuiController;
import schedulegui.gui.CallbackInterface;
import java.util.*;
import java.text.*;

/**
 * Write a description of class App here.pu
 * 
 * @author Eric de Potter (App)
 * @version (a version number or a date)
 */
public class App
{
    private Statistiek statistics;
    private Setup setup;
    private GuiController gui;
    private SearchEngine searchEngine;
    private boolean personal;
    /**
     * Constructor for objects of class App
     */
    public App()
    {
        statistics = new Statistiek();
        setup = new Setup();
        Database.replaceOptredens(setup.getAlleOptredens());
        searchEngine = new SearchEngine();
        gui = new GuiController(new GuiEngine());
        
        //initialiseert de gui
        gui.setSchedule(convertToSchedule(Database.getOptredens()));
        gui.setTime(convertToDate("19:00"));
        gui.showGui();
        
        gui.sleep(2000);
        
        personal = false;
    }
    
    /**
     * Handelt de aanmelding van een gebruiker af
     * 
     * @return  true als het gelukt is, anders false
     */
    private boolean login()
    {
        Gebruiker userLogin = null;
        String username = gui.showInputPanel("Vul je gebruikersnaam in (type cancel om te annuleren):");
        boolean success = false;
        boolean finished = false;
        boolean finished1 = false;
        boolean cancel = false;
        while (!finished) {
            if (checkUserInput(username)) {
                List<Gebruiker> users = Database.getUsers();
                int index = 0;
                boolean found = false;
                if (username.toLowerCase().equals("cancel")) {
                    finished = true;
                    finished1 = true;
                    found = true;
                    cancel = true;
                }
                while (index < users.size() && !found ){
                    Gebruiker user = users.get(index);
                    if (user.getName().equals(username)) {
                        userLogin = user;
                        found = true;
                        finished = true;
                    }
                    index++;
                }
                if (userLogin == null && !cancel)
                        username = gui.showInputPanel("Foute gebruikersnaam (type cancel om te annuleren):");
            }
            else
                username = gui.showInputPanel("Vul minstens 1 cijfer of getal in:");
        }
        String password = "";
        if (userLogin == null && !cancel) {
            gui.showMessagePanel("Er is iets misgelopen bij het zoeken van de overeenkomstige gebruiker");
            return false;
        }
        
        else if (userLogin != null)
            password = gui.showPasswordPanel("Vul het wachtwoord in voor "+userLogin.getName()+" (type cancel om te annuleren):");
        while (!finished1) {
            if (checkUserInput(password)) {
                if (password.toLowerCase().equals("cancel")) {
                    finished1 = false;
                }
                if (userLogin.getWachtwoord().equals(password)) {
                    Database.setCurrentUser(userLogin);
                    gui.showMessagePanel("Je bent successvol ingelogd!");
                    success = true;
                    finished1 = true;
                }
                else
                    password = gui.showPasswordPanel("Fout wachtwoord voor "+userLogin.getName()+":");
            }

            else
                password = gui.showInputPanel("Vul minstens 1 cijfer of getal in:");
        }
        
        if (success) {
            gui.setSchedule(convertToSchedule(Database.getOptredens()));
            gui.setTime(convertToDate("19:00"));
            gui.updateSchedule();
            
            personal = false;
            
            return chooseSchermnaam();
        }
        else
            return false;
    }
    
    /**
     * Zorgt voor de registratie van een gebruiker
     * Na de registratie is de gebruiker ook direct aangemeld
     * 
     * @return  true als het gelukt is anders false
     */
    private boolean register()
    {
        String username = gui.showInputPanel("Geef een gebruikersnaam in (type cancel om te annuleren):");
        boolean finished = false;
        boolean correct = false;
        boolean finished1;
        boolean cancel = false;
        while (!finished) {
            if (checkUserInput(username)) {
                List<Gebruiker> users = Database.getUsers();
                int index = 0;
                boolean inUse = false;
                if (username.toLowerCase().equals("cancel")) {
                    inUse = true;
                    finished = true;
                    correct = true;
                    cancel = true;
                }
                while(index < users.size() && !inUse) {
                    if (users.get(index).getName().equals(username)) {
                        inUse = true;
                        username = gui.showInputPanel("Gebruikersnaam is al in gebruik:");
                    }
                    index++;
                }
                if (!inUse)
                    finished = true;
            }
            else
                username = gui.showInputPanel("Vul minstens 1 cijfer of getal in:");
        }
        String password;
        while (!correct) {
            finished = false;
            finished1 = false;
            password = gui.showPasswordPanel("Geef een wachtwoord in (type cancel om te annuleren):");
            if (password.toLowerCase().equals("cancel")) {
                finished = true;
                finished1 = true;
                correct = true;
                cancel = true;
            }
            while (!finished) {
                if (checkUserInput(password))
                    finished = true;
                else
                    password = gui.showPasswordPanel("Vul minstens 1 cijfer of getal in:");
            }
            String checkPassword = gui.showPasswordPanel("Geef je wachtwoord opnieuw in (type cancel om te annuleren):");
            if (checkPassword.toLowerCase().equals("cancel")) {
                finished = true;
                finished1 = true;
                correct = true;
                cancel = true;
            }
            while (!finished1) {
                if (checkUserInput(checkPassword))
                    finished1 = true;
                else
                    password = gui.showPasswordPanel("Vul minstens 1 cijfer of getal in:");
            }
            
            if(password.equals(checkPassword) && !(password.toLowerCase().equals("cancel"))) {
                Gebruiker newUser = new Gebruiker(username, password);
                Database.addUser(newUser);
                Database.setCurrentUser(newUser);
                gui.showMessagePanel("Je bent successvol geregistreerd!");
                correct = true;
            }      
            else if (!(password.toLowerCase().equals("cancel")) || !(checkPassword.toLowerCase().equals("cancel")))
                gui.showMessagePanel("De wachtwoorden komen niet overeen.");
            
        }
        
        if (correct && !cancel) {
            gui.setSchedule(convertToSchedule(Database.getOptredens()));
            gui.setTime(convertToDate("19:00"));
            gui.updateSchedule();
            
            personal = false;
            
            return chooseSchermnaam();
        }
        else
            return false;
    }
    
    /**
     * Laat de gebuiker toe om een schermnaam in te vullen voor dit bezoek
     * 
     * @return true als het gelukt is om een schermnaam te kiezen anders false
     */
    private boolean chooseSchermnaam()
    {
        String name = gui.showInputPanel("Geef een schermnaam in voor dit bezoek:");
        boolean finished = false;
        while (!finished) {
            if (checkUserInput(name)) {
                Database.getCurrentUser().setSchermnaam(name);
                return true;
            }
            else
                name = gui.showInputPanel("Vul minstens 1 cijfer of getal in:");
        }
        return false;
    }
    
    /**
     * gaat na of de tekst leeg is of niet
     * 
     * @param  question  de tekst die de gebruiker ziet in de inputbox
     * @return true als de string niet alleen uit spaties bestaat, anders false
     */
    private boolean checkUserInput(String input)
    {
        input = input.trim();
        if (input.length() == 0)
            return false;
        return true;
    }
    
    /**
     * Controleert of het optreden overlapt met een ander optreden van de huidige gebruiker
     * 
     * @param optreden het optreden dat gecontroleerd
     * @return  true als ze overlappen anders false
     */
    private boolean overlap(Optreden newOptreden)
    {
        List<Optreden> optredens = Database.getCurrentUser().getOptredens();
        //maakt een calender van de start- en stopDate
        Calendar newStart = Calendar.getInstance();
        newStart.setTime(newOptreden.getStartDate());
        Calendar newStop = Calendar.getInstance();
        newStop.setTime(newOptreden.getStopDate());
        for (Optreden optreden: optredens) {
            //maakt een calender van de start- en stopDate
            Calendar start = Calendar.getInstance();
            start.setTime(optreden.getStartDate());
            Calendar stop = Calendar.getInstance();
            stop.setTime(optreden.getStopDate());
            if (newStart.compareTo(start) <= 0) {
                if (newStop.after(start)) {
                    return true;
                }
            }
            else if (newStart.before(stop)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Zet een lijst van optredens om in een map<String, List<InfoItem>>
     * 
     * @param optredens  de lijst van optredens
     * @return  de overeenkomstige map
     */
    private Map<String, List<InfoItem>> convertToSchedule (List<Optreden> optredens)
    {
        Map<String, List<InfoItem>> schedule = new LinkedHashMap<String, List<InfoItem>>();
        List<InfoItem> mainstage = new ArrayList<InfoItem>();
        List<InfoItem> marquee = new ArrayList<InfoItem>();
        List<InfoItem> club = new ArrayList<InfoItem>();
        for (Optreden optreden : optredens) {
            if (optreden.getStage().toLowerCase().equals("main stage")) 
                mainstage.add(optreden);
            else if (optreden.getStage().toLowerCase().equals("marquee"))
                marquee.add(optreden);
            else if (optreden.getStage().toLowerCase().equals("club"))
                club.add(optreden);
        }
        schedule.put("MAIN STAGE", mainstage);
        schedule.put("MARQUEE", marquee);
        schedule.put("CLUB", club);
        
        return schedule;
    }
    
    /**
     * Geeft een mooie tekst van de gegeven array van optredens
     * 
     * @param optredens  een gesorteerde array met optredens
     * @return  een mooie tekst
     */
    private String showStatistics(Optreden[] optredens)
    {
        int index = 0;
        String returnString = "";
        while (index < optredens.length) {
            returnString += "    " + (index+1) + ") " + optredens[index].getTitle() + "\n";
            index++;
        }
        return returnString;
    }
    
    /**
     * Geeft een tekst terug van de gegeven lijst van commentaren
     * 
     * @param comments  de lijst van commentaren
     * @return een tekst met de commentaren
     */
    private String showListComments(List<Commentaar> comments)
    {
        String returnString = "";
        for (Commentaar comment : comments)
            returnString += comment.getTekst() + "\n";
        return returnString;
    }
    
    /**
     * Geeft een tekst terug van de gegeven lijst van commentaren
     * 
     * @param comments  de lijst van commentaren
     * @return een tekst met de commentaren
     */
    private String showListCommentsUser(List<Commentaar> comments)
    {
        String returnString = "";
        for (Commentaar comment : comments)
            returnString += comment.getOptreden().getTitle() + ": " + comment.getTekst() + "\n";
        return returnString;
    }
    
    /**
     * Vormt een string van de vorm "HH:mm" om in een date-object.
     * 
     * @param  String   De string, van de vorm "HH:mm" om een date-object van te maken
     * @return     een date-object van de meegegeven string
     */
    public static Date convertToDate(String dateString)
    {
        String dateFormat = "HH:mm";
        //construct a SimpleDateFormat object with given dateFormat
        SimpleDateFormat format = new SimpleDateFormat(dateFormat);
        //start parsing at position 0 in the string
        ParsePosition pos = new ParsePosition(0);
        //on success, returns Date object for date in dateString
        //on failure, returns null
        Date date = format.parse(dateString, pos);
        if (date == null)
            return null;
        //turn into calendar
        Calendar parsedCal = Calendar.getInstance();
        parsedCal.setTime(date);
        //now
        Calendar newCal = Calendar.getInstance();
        //add hours, minutes
        newCal.set(Calendar.HOUR_OF_DAY, parsedCal.get(Calendar.HOUR_OF_DAY));
        newCal.set(Calendar.MINUTE, parsedCal.get(Calendar.MINUTE));
        //turn into 
        Date newDate = newCal.getTime();
        return newDate;
        //return date;
    }
    
    
    
    
    
    
    public class GuiEngine implements CallbackInterface
    {
        /**
         * Constructor for objects of class GuiEngine
         */
        public GuiEngine()
        {
        }
        
        /**
         * Voegt een commentaar toe aan een optreden.
         * 
         * @param   Het optreden waar een commentaar aan toegevoegd wil worden.
         */
        public void addComment(InfoItem infoitem)
        {
            if (Database.getCurrentUser() == null)
                    gui.showMessagePanel("U moet eerst aangemeld zijn.");
            else
            {
                Optreden optreden = (Optreden)infoitem;
                Gebruiker user = Database.getCurrentUser();
                String tekst = gui.showInputPanel("Schrijf hier je commentaar.");
                boolean finished = false;
                while (finished == false)
                {
                    if (checkUserInput(tekst))
                    {
                        Commentaar comment = new Commentaar(user, optreden, tekst);
                        Database.addComment(comment);
                        finished = true;
                    }
                    else
                        tekst = gui.showInputPanel("Schrijf hier je commentaar.");
                }
            }
        }
        
        /**
         * Laat alle commentaren zien van een optreden.
         * 
         * @param   Het optreden waarvan de commentaren gezien willen worden.
         */
        public void showComments(InfoItem infoitem)
        {
            Optreden optreden = (Optreden)infoitem;
            List<Commentaar> comments = Database.getComments(optreden);
            String text = "";
            String title = "Alle commentaren voor " + optreden.getTitle();
            for (Commentaar comment: comments)
            {
                text += comment.getTekst() + "\n";
            }
            gui.showTextPanel(title, text);            
        }
        
        /**
         * Laat de details van een optreden zien.
         * 
         * @param   Het optreden waarvan de details gezien willen worden.
         */
        public void showDetails(InfoItem infoitem)
        {
            String text = "Details voor " + infoitem.getTitle();
            gui.showTextPanel(text, infoitem.toString());
        }
        
        /**
         * Methode om bij een optreden aanwezig te zijn.
         * 
         * @param   Het optreden
         */
        public void attendItem(InfoItem infoitem)
        {
            if (Database.getCurrentUser() == null)
                gui.showMessagePanel("U moet eerst aangemeld zijn.");
            else
            {
                if (overlap((Optreden)infoitem) == true)

                    gui.showMessagePanel("De optredens overlappen elkaar.");
                else
                    Database.getCurrentUser().addOptreden((Optreden)infoitem);
            }
        }
         /**
          * Methode om niet meer aanwezig te zijn bij een optreden.
          * 
          * @param   het optreden
          */
        public void unattendItem(InfoItem infoitem)
        {
            if (Database.getCurrentUser() == null)
                gui.showMessagePanel("U moet eerst aangemeld zijn.");
            else
                Database.getCurrentUser().removeOptreden((Optreden)infoitem);
        }
        
        /**
         * Methode om een optreden te like
         * 
         * @param   Het optreden
         */
        public void likeItem(InfoItem infoitem)
        {
            Optreden optreden = (Optreden)infoitem;
            if (Database.getCurrentUser() == null)
                gui.showMessagePanel("U moet eerst aangemeld zijn.");
            else
                optreden.like(Database.getCurrentUser());
        }
        
        /**
         * Methode om een optreden te unliken.
         * 
         * @param   Het optreden
         */
        public void unlikeItem(InfoItem infoitem)
        {
            Optreden optreden = (Optreden)infoitem;
            if (Database.getCurrentUser() == null)
                gui.showMessagePanel("U moet eerst aangemeld zijn.");
            else
                optreden.dislike(Database.getCurrentUser());   
        }
        
        /**
         * Laat het hoofd menu zien wanneer erop geklikt wordt.
         */
        public void showMenu()
        {
            Set<String>menu = new LinkedHashSet<String>();
            menu.add("Algemene planning");
            menu.add("Persoonlijke planning");
            menu.add("User management");
            menu.add("Statistieken");
            menu.add("Zoeken naar commentaren");
            menu.add("Cancel");
            String choice = gui.showChoicePanel("Menu", menu);
            showMenuHandler(choice);
        }
        
        /**
         * Bepaalt de keuze van het hoofdmenu.
         * 
         * @param   De string die terug gegeven wordt door de choicepanel in het hoofdmenu.
         */
        private void showMenuHandler(String menu)
        {
            if (menu.equals("Algemene planning"))
            {
                List<Optreden> alleoptredens = Database.getOptredens();
                gui.setSchedule(convertToSchedule(alleoptredens));
                gui.updateSchedule();
                personal = false;
            }
            else if (menu.equals("Persoonlijke planning"))
            {
                if (Database.getCurrentUser() == null)
                    gui.showMessagePanel("U moet eerst aangemeld zijn.");
                else
                {
                    List<Optreden> optredens = Database.getCurrentUser().getOptredens();
                    gui.setSchedule(convertToSchedule(optredens));
                    gui.updateSchedule();
                    personal = true;
                }
            }
            else if (menu.equals("User management"))
            {
                Set<String>menu2 = new LinkedHashSet<String>();
                menu2.add("Aanmelden");
                menu2.add("Wisselen van gebruiker");
                menu2.add("Registreren");
                menu2.add("Cancel");
                String choice = gui.showChoicePanel("User managment", menu2);
                showMenu2Handler(choice);
            }
            else if (menu.equals("Statistieken"))
            {
                Optreden[] liked = statistics.getMostLiked(3);
                String statistic = "Top 3 likes: " + "\n" + showStatistics(liked);              
                Optreden[] attendant = statistics.getMostAttended(3);
                statistic += "Top 3 publiek: " + "\n" + showStatistics(attendant);
                Optreden[] comments = statistics.getMostComments(3);
                statistic += "Top 3 commentaar: " + "\n" + showStatistics(comments);
                gui.showTextPanel("Statistieken: ", statistic);
            }
            else if (menu.equals("Zoeken naar commentaren"))
            {
                String search = gui.showInputPanel("Van welke gebruiker wil je de commentaren zien? ");
                Map<Gebruiker, List<Commentaar>> usermap = searchEngine.searchCommentaren(search);
                Set<Gebruiker> keys = usermap.keySet();
                if (keys.size() == 0)
                    gui.showMessagePanel("Er is niets gevonden.");
                else if (keys.size() == 1)
                {
                    Gebruiker[] users = keys.toArray(new Gebruiker[0]);
                    Gebruiker user = users[0];
                    gui.showTextPanel("De commentaren van " + user.getSchermnaam(), showListComments(usermap.get(user)));
                }
                else 
                {
                    Set<String> schermnamen = new LinkedHashSet<String>();
                    for (Gebruiker gebruiker : keys)
                        schermnamen.add(gebruiker.getSchermnaam() + ": " + gebruiker.getName());
                    String choice = gui.showChoicePanel("Selecteer de gebruiker.", schermnamen);
                    String[] splitchoice = choice.split(":");
                    String username = splitchoice[1].replaceFirst(" ", "");
                    for (Gebruiker gebruiker : keys)
                    {
                        if (gebruiker.getName().equals(username))
                            gui.showTextPanel(gebruiker.getSchermnaam(), showListCommentsUser(usermap.get(gebruiker)));
                    }
                }
            }
            else if (menu.equals("cancel"))
            {}
        }
        
        /**
         * Bepaalt de keuze van het 2e menu.
         * 
         * @param   De string die terug gegeven wordt door het choicepanel van het 2e menu.
         */
        private void showMenu2Handler(String menu2)
        {
            if (menu2.equals("Aanmelden"))
                login();
            else if (menu2.equals("Wisselen van gebruiker"))
                login();
            else if (menu2.equals("Registreren"))
                register();
            else if (menu2.equals("Cancel"))
            {}
        }
        
        /**
         * Verandert de tijd van de gui.
         */
        public void changeDate()
        {
            String date = gui.showInputPanel("Geef een nieuwe datum in (HH:mm): ");
            Date newdate = convertToDate(date);
            if (newdate != null) {
                gui.setTime(newdate);
                gui.updateSchedule();
            }
        }
        
        /**
         * Zoekt optredens. Indien meerdere mogelijkheden wordt er een choicepanel getoont.
         */
        public void searchItem(String searchstring)
        {
            List<Optreden> gevondenoptredens = searchEngine.searchOptreden(searchstring,personal);
            Set<String> optredens = new HashSet<String>();
            if (gevondenoptredens.size() == 0)
                gui.showMessagePanel("Er is geen optreden gevonden.");   
            else
            {
                if (gevondenoptredens.size() == 1)
                {
                    gui.setTime(gevondenoptredens.get(0).getStartDate());               
                    gui.updateSchedule();
                }
                else
                {
                    for (Optreden gevondenoptreden : gevondenoptredens)
                        optredens.add(gevondenoptreden.getTitle());
                    String choice = gui.showChoicePanel("Welk optreden wil je bekijken? ", optredens);
                    for (Optreden gevondenoptreden : gevondenoptredens)
                    {
                        if (gevondenoptreden.getTitle().equals(choice))
                        {
                            gui.setTime(gevondenoptreden.getStartDate());
                            gui.updateSchedule();
                        }
                    }
                }
            }
        }
    }
}
